import java.util.Arrays;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author :sunwei
 * @Description:
 * @Date create in 16:10  2021/5/27
 * @Modified by:
 */
public class MarsCalcuator {

    public int getType(String value) {
        switch (value) {
            case "#":
                return 3;
            case "$":
                return 2;
            default:
                return -1;
        }
    }

    public double calcInner(String value, Number num1, Number num2) {
        int type = getType(value);
        if (type == -1) {
            throw new RuntimeException("expression error");
        }
        switch (type) {
            case 3:
                return num1.doubleValue() * num2.doubleValue() - 3;
            case 2:
                return (num1.doubleValue() + 2) * (num2.doubleValue() * num2.doubleValue());
            default:
                throw new RuntimeException("expression error");
        }
    }

    public Number calc(String expression) {
        Stack<String> opStack = new Stack();
        Stack<Number> valStack = new Stack();

        String exp = "^[0-9]";
        Pattern compile = Pattern.compile(exp);

        validateExpression(expression);
        String[] split = expression.split("|");
        String aheadValue = "";
        for (int i = 0; i < split.length; i++) {
            String str = split[i];
            if (!aheadValue.equals("")) {
                aheadValue = split[i - 1];
            } else {
                aheadValue = split[i];
            }
            if (getType(str) == -1) {
                Double intVal = Double.valueOf(str);
                if (compile.matcher(aheadValue).find()) {
                    if (!valStack.isEmpty()) {

                        Number val = valStack.pop();
                        double value = val.doubleValue() * 10 + intVal;
                        valStack.push(value);
                    } else {
                        valStack.push(intVal);
                    }
                } else {
                    valStack.push(intVal);
                }
            } else {
                if (getType(str) < getType(opStack.peek())) {
                    String op = opStack.pop();
                    Number num2 = valStack.pop();
                    Number num1 = valStack.pop();
                    double result = calcInner(op, num1, num2);
                    valStack.push(result);
                    opStack.push(str);
                } else {
                    opStack.push(str);
                }
            }
        }

        if (!opStack.isEmpty()) {
            for (String op : opStack) {
                Number num2 = valStack.pop();
                Number num1 = valStack.pop();
                double result = calcInner(op, num1, num2);
                valStack.push(result);
            }
        }

        return valStack.peek();
    }

    public void validateExpression(String expression) {
        String exp = "^[0-9]";
        Pattern compile = Pattern.compile(exp);
        if (expression == null || expression.length() == 0) {
            throw new RuntimeException("expression must be not null!");
        }
        char lastChar = expression.charAt(expression.length() - 1);
        char firstChar = expression.charAt(0);

        if (getType(String.valueOf(lastChar)) != -1 || getType(String.valueOf(firstChar)) != -1) {
            throw new RuntimeException("expression error ,first char and last char can not be an operation");
        }
        String[] split1 = expression.split("#");
        String[] split2 = expression.split("\\$");

        boolean flag1 = Arrays.asList(split1).stream().allMatch((i) -> i.length() == 0 || !compile.matcher(i).find());
        boolean flag2 = Arrays.asList(split2).stream().allMatch((i) -> i.length() == 0 || !compile.matcher(i).find());

        if (flag1 || flag2) {
            throw new RuntimeException("expression error , expression operation can not be together!");
        }

    }

}
